package com.pokermind.dse.tcp;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.pokermind.dse.tcp.client.SimpleTcpClient;
import com.pokermind.dse.tcp.protocol.ProtocolConstants;
import com.pokermind.model.dto.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

/**
 * TCP客户端独立程序 - 完整版本
 *
 * 与RealPokerHandFlowTest完全一致的德州扑克手牌流程 包含完整的请求/响应日志记录和连接/手牌ID跟踪
 *
 * @author PokerMind Team
 */
public class TcpClientMain {

    private static final Logger logger = LoggerFactory.getLogger(TcpClientMain.class);
    private static final ObjectMapper objectMapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT);
    private static final ObjectMapper compactMapper = new ObjectMapper();  // 紧凑格式，用于单行JSON

    // 连接和手牌标识 - 用于多牌局日志跟踪
    private static final String CONNECTION_ID = "CONN_" + System.currentTimeMillis();
    private static final Scanner scanner = new Scanner(System.in);

    private static SimpleTcpClient client;
    private static String currentHandIdStr;
    private static String currentStreet = "preflop";  // 当前街道，默认preflop
    
    // 追踪每个玩家在当前手牌的累计投入（用于计算fold时的损失）
    private static final Map<String, BigDecimal> playerInvestments = new HashMap<>();

    /**
     * JSON格式化打印方法
     */
    private static String prettyPrintJson(String json) {
        try {
            Object jsonObject = objectMapper.readValue(json, Object.class);
            return objectMapper.writeValueAsString(jsonObject);
        } catch (Exception e) {
            return json; // 失败时返回原始JSON
        }
    }
    
    /**
     * 统一发送请求并记录完整日志的方法 - 支持RequestId追踪
     */
    private static String sendRequestWithFullLogging(short protocolId, Object requestData, int timeout,
        TimeUnit timeUnit, String operationName) throws Exception {
        
        String requestJson = objectMapper.writeValueAsString(requestData);
        logger.info("\n[{}] [HandID:{}] {} Request:", CONNECTION_ID, currentHandIdStr, operationName);
        logger.info("{}", prettyPrintJson(requestJson));

        CompletableFuture<String> responseFuture = client.sendMessage(protocolId, requestData, timeout, timeUnit);
        String responseJson = responseFuture.get();
        
        JsonNode responseNode = objectMapper.readTree(responseJson);
        String serverReqId = responseNode.has("rqid") ? 
            responseNode.get("rqid").asText().substring(0, 8) : "unknown";

        logger.info("[{}] [HandID:{}] [ReqID:{}] {} Response:", 
            CONNECTION_ID, currentHandIdStr, serverReqId, operationName);
        logger.info("{}", prettyPrintJson(responseJson));

        return responseJson;
    }
    
    /**
     * 完整的action记录日志 - 结构化、直观的格式（含请求/响应/reqId）
     */
    private static String sendActionWithSimpleLogging(Object requestData, String actionDesc) throws Exception {
        // 提取关键信息
        @SuppressWarnings("unchecked")
        Map<String, Object> reqMap = (Map<String, Object>) requestData;
        String playerId = (String) reqMap.get("player_id");
        String street = (String) reqMap.get("street");
        String actionType = (String) reqMap.get("action_type");
        BigDecimal amount = (BigDecimal) reqMap.get("amount");
        BigDecimal stackAfter = (BigDecimal) reqMap.get("stack_after");
        BigDecimal potAfter = (BigDecimal) reqMap.get("pot_after");
        
        // 翻译street
        String streetChinese = translateStreet(street);
        
        // 构建行动摘要
        String actionSummary = String.format("%s%s", 
            actionType, 
            amount != null && amount.compareTo(BigDecimal.ZERO) > 0 ? " " + amount : "");
        
        // 格式化请求JSON（紧凑单行）
        String requestJson = compactMapper.writeValueAsString(requestData);
        
        // 发送请求
        CompletableFuture<String> responseFuture = 
            client.sendMessage(ProtocolConstants.RECORD_ACTION_REQ, requestData, 10, TimeUnit.SECONDS);
        String responseJson = responseFuture.get();
        
        // 解析响应
        JsonNode responseNode = objectMapper.readTree(responseJson);
        int code = responseNode.get("code").asInt();
        String desc = responseNode.get("desc").asText();
        String reqId = responseNode.has("rqid") ? responseNode.get("rqid").asText() : "N/A";
        
        // 更新玩家累计投入
        BigDecimal currentInvestment = playerInvestments.getOrDefault(playerId, BigDecimal.ZERO);
        if (amount != null && amount.compareTo(BigDecimal.ZERO) > 0) {
            currentInvestment = currentInvestment.add(amount);
            playerInvestments.put(playerId, currentInvestment);
        }
        
        // 计算fold时的损失
        String lossInfo = "";
        if ("fold".equalsIgnoreCase(actionType) && currentInvestment.compareTo(BigDecimal.ZERO) > 0) {
            lossInfo = String.format("\n本手牌累计投入: %s (已损失)", currentInvestment);
        }
        
        // 打印结构化的行动信息（包含请求、响应、reqId）
        logger.info("\n" +
            "================================================================================\n" +
            "[{}] [HandID:{}] [{}阶段] 玩家行动 [ReqID:{}]\n" +
            "--------------------------------------------------------------------------------\n" +
            "玩家: {}\n" +
            "行动: {}\n" +
            "本次投入: {}{}\n" +
            "行动后余额: {}\n" +
            "行动后底池: {}\n" +
            "--------------------------------------------------------------------------------\n" +
            "请求: {}\n" +
            "返回: code={}, desc={}, reqId={}\n" +
            "================================================================================",
            CONNECTION_ID, currentHandIdStr, streetChinese, reqId,
            playerId,
            actionSummary,
            amount != null && amount.compareTo(BigDecimal.ZERO) > 0 ? amount : "0",
            lossInfo,
            stackAfter,
            potAfter,
            requestJson,
            code,
            desc,
            reqId
        );
        
        // 打印结果
        if (code == 200) {
            logger.info("[{}] [HandID:{}] >>> 执行成功 ✓\n", CONNECTION_ID, currentHandIdStr);
        } else {
            logger.error("[{}] [HandID:{}] >>> 执行失败 ✗ - {}\n", CONNECTION_ID, currentHandIdStr, desc);
        }
        
        if (code != 200) {
            throw new RuntimeException("行动记录失败: " + desc);
        }
        
        return responseJson;
    }
    
    /**
     * 翻译street为中文
     */
    private static String translateStreet(String street) {
        if (street == null) return "未知";
        switch (street.toLowerCase()) {
            case "preflop": return "翻牌前";
            case "flop": return "翻牌圈";
            case "turn": return "转牌圈";
            case "river": return "河牌圈";
            default: return street;
        }
    }
    
    /**
     * 翻译行动类型为中文
     */
    private static String translateAction(String actionType) {
        switch (actionType.toLowerCase()) {
            case "fold": return "弃牌";
            case "check": return "过牌";
            case "call": return "跟注";
            case "bet": return "下注";
            case "raise": return "加注";
            case "all_in": return "全压";
            case "small_blind": return "小盲";
            case "big_blind": return "大盲";
            default: return actionType;
        }
    }

    public static void main(String[] args) {
        logger.info("Starting TCP Client Program - Multi-Round Testing Supported");
        logger.info("Connection ID: {}", CONNECTION_ID);

        // 解析命令行参数
        String host = "127.0.0.1";
        int port = 8889;
        String apiKey = "pk_test_demo00000000000000000000000000000000"; // 默认测试Key
        
        if (args.length >= 2) {
            host = args[0];
            port = Integer.parseInt(args[1]);
        }
        if (args.length >= 3) {
            apiKey = args[2]; // 可选的API Key参数
        }
        logger.info("Target Server: {}:{}", host, port);

        try {
            // 1. 建立连接
            connectToServer(host, port);
            
            // 2. 认证
            authenticateWithServer(apiKey);
            
            int roundCount = 1;
            boolean continueTest = true;
            
            while (continueTest) {
                logger.info("\n=============== Starting Round {} Test ===============", roundCount);
                
                try {
                    // 2. 执行完整流程
                    executeCompleteFlow();
                    
                    logger.info("=============== Round {} Test Completed ===============", roundCount);
                    
                    // 询问用户是否继续下一轮
                    continueTest = askUserToContinue(roundCount);
                    
                    if (continueTest) {
                        roundCount++;
                        // 重置状态准备下一轮
                        resetForNextRound();
                    }
                    
                } catch (Exception e) {
                    logger.error("[{}] Round {} Test Failed: {}", CONNECTION_ID, roundCount, e.getMessage(), e);
                    
                    // 询问是否在失败后继续
                    System.out.print("测试失败，是否继续下一轮? (y/n): ");
                    try {
                        String input = scanner.nextLine().trim().toLowerCase();
                        continueTest = "y".equals(input) || "yes".equals(input);
                        if (continueTest) {
                            roundCount++;
                            resetForNextRound();
                        }
                    } catch (Exception inputEx) {
                        continueTest = false;
                    }
                }
            }
            
            logger.info("Multi-round testing completed, total rounds: {}", roundCount - 1);

        } catch (Exception e) {
            logger.error("[{}] 客户端执行失败: {}", CONNECTION_ID, e.getMessage(), e);
        } finally {
            if (client != null) {
                client.disconnect();
            }
            scanner.close();
            logger.info("[{}] TCP Client Program Ended", CONNECTION_ID);
        }
    }
    
    /**
     * 询问用户是否继续下一轮测试
     */
    private static boolean askUserToContinue(int completedRounds) {
        System.out.print("\nRound " + completedRounds + " completed. Continue with next round? (y/n): ");
        try {
            String input = scanner.nextLine().trim().toLowerCase();
            return "y".equals(input) || "yes".equals(input);
        } catch (Exception e) {
            return false;
        }
    }
    
    /**
     * 重置状态准备下一轮测试
     */
    private static void resetForNextRound() {
        currentHandIdStr = null;
        currentHandIdStr = null;
        currentStreet = "preflop";  // 重置街道为preflop
        logger.info("State reset, ready for next round testing");
    }

    private static void connectToServer(String host, int port) throws Exception {
        logger.info("[{}] Connecting to TCP server {}:{}...", CONNECTION_ID, host, port);
        client = new SimpleTcpClient(host, port, CONNECTION_ID);  // 传递统一的连接ID
        client.connect().get(5, TimeUnit.SECONDS);
        logger.info("[{}] Connected successfully!", CONNECTION_ID);
    }
    
    /**
     * 认证流程 - 使用API Key认证
     */
    private static void authenticateWithServer(String apiKey) throws Exception {
        logger.info("[{}] Authenticating with API Key...", CONNECTION_ID);
        
        // 构造认证请求
        AuthRequest authRequest = AuthRequest.builder()
            .apiKey(apiKey)
            .clientVersion("1.0.0")
            .clientName("PokerMind-JavaClient")
            .build();
        
        // 发送认证请求
        String responseJson = sendRequestWithFullLogging(
            ProtocolConstants.AUTH_REQ,
            authRequest,
            10,
            TimeUnit.SECONDS,
            "认证"
        );
        
        // 解析响应
        JsonNode responseNode = objectMapper.readTree(responseJson);
        int code = responseNode.get("code").asInt();
        
        if (code == 200) {
            JsonNode data = responseNode.get("data");
            String sessionId = data.has("session_id") ? data.get("session_id").asText() : "unknown";
            String clientId = data.has("client_id") ? data.get("client_id").asText() : "unknown";
            
            logger.info("[{}] ✓ Authentication successful!", CONNECTION_ID);
            logger.info("    Session ID: {}", sessionId);
            logger.info("    Client ID: {}", clientId);
            
            // 显示权限和配额信息
            if (data.has("permissions")) {
                logger.info("    Permissions: {}", data.get("permissions"));
            }
            if (data.has("quota")) {
                logger.info("    Quota: {}", data.get("quota"));
            }
        } else {
            String errorMsg = responseNode.has("desc") ? responseNode.get("desc").asText() : "Unknown error";
            logger.error("[{}] ✗ Authentication failed: {}", CONNECTION_ID, errorMsg);
            throw new RuntimeException("Authentication failed: " + errorMsg);
        }
    }

    private static void executeCompleteFlow() throws Exception {
        logger.info("[{}] \n 开始完整扑克手牌流程 - 与RealPokerHandFlowTest完全一致", CONNECTION_ID);

        // 步骤1: 心跳测试
        step1_Heartbeat();
        waitForInput();

        // 步骤2: 创建手牌 (6人桌)
        step2_CreateHand();
        waitForInput();

        // 步骤3: Preflop行动阶段
        step3_RecordPreflopActions();
        waitForInput();

        // 步骤4: 转换到Flop
        step4_TransitionToFlop();
        waitForInput();

        // 步骤5: 记录Flop行动
        step5_RecordFlopActions();
        waitForInput();

        // 步骤6: 转换到Turn
        step6_TransitionToTurn();
        waitForInput();

        // 步骤7: 记录Turn行动
        step7_RecordTurnActions();
        waitForInput();

        // 步骤8: 转换到River
        step8_TransitionToRiver();
        waitForInput();

        // 步骤9: 记录River行动
        step9_RecordRiverActions();
        waitForInput();

        // 步骤10: AI分析 (跳过)
        step10_AIAnalysisSkipped();
        waitForInput();

        // 步骤11: 完成手牌
        step11_CompleteHand();

        logger.info("[{}] [HandID:{}] \n 完整流程执行完成！", CONNECTION_ID, currentHandIdStr);
    }

    private static void step1_Heartbeat() throws Exception {
        logger.info("[{}] \n步骤1: 心跳测试", CONNECTION_ID);

        Map<String, Object> heartbeatData = Map.of("timestamp", System.currentTimeMillis());

        String result =
            sendRequestWithFullLogging(ProtocolConstants.HEARTBEAT_REQ, heartbeatData, 5, TimeUnit.SECONDS, "心跳测试");

        JsonNode json = objectMapper.readTree(result);

        // 验证新的响应格式：rqid, code, desc, data
        if (!json.has("data")) {
            throw new IllegalArgumentException("Response missing 'data' field");
        }
        if (!json.has("rqid") || !json.has("code") || !json.has("desc")) {
            logger.warn("[{}] 响应格式不完整: {}", CONNECTION_ID, result);
        }

        JsonNode dataNode = json.get("data");
        int code = json.get("code").asInt();
        String desc = json.get("desc").asText();
        
        // 新协议：成功响应code=200，心跳响应的data为null
        if (code == 200 && "heartbeat".equals(desc)) {
            logger.info("[{}] 服务器状态正常 - 心跳响应成功", CONNECTION_ID);
        } else {
            logger.warn("[{}] 服务器状态异常: code={}, desc={}", CONNECTION_ID, code, desc);
        }
    }

    private static void step2_CreateHand() throws Exception {
        logger.info("[{}] \n 步骤2: 创建手牌 (6人桌，Hero在按钮位) - 与RealPokerHandFlowTest完全一致", CONNECTION_ID);

        // 创建新手牌，重置街道状态和玩家投入记录
        currentStreet = "preflop";
        playerInvestments.clear();  // 清空上一手牌的投入记录
        
        CreateHandRequest request = new CreateHandRequest();
        currentHandIdStr = "TEST_FLOW_H_" + System.currentTimeMillis();
        request.setHandId(currentHandIdStr);
        request.setTableId("NL200_6MAX_TEST");
        request.setButtonSeat(6);  // Hero在按钮位
        // V4.8: small_blind 和 big_blind 字段已移除
        // 盲注由前端通过 RECORD_ACTION_REQ 主动发起
        request.setAnte(BigDecimal.ZERO);

        // 6人桌玩家配置 - 与RealPokerHandFlowTest完全一致 
        // 前端驱动模式：只提供基础信息，不提供actionOrder
        List<CreateHandRequest.PlayerInfo> players =
            Arrays.asList(createPlayer("test_sb_001", "TestSB", 1, 20000, false, "SB", null),     // 小盲位
                createPlayer("test_bb_002", "TestBB", 2, 19800, false, "BB", null),     // 大盲位 (修正stack值)
                createPlayer("test_utg_003", "TestUTG", 3, 21500, false, "UTG", null),  // Under the Gun
                createPlayer("test_mp_004", "TestMP", 4, 18200, false, "MP", null),     // Middle Position
                createPlayer("test_co_005", "TestCO", 5, 22800, false, "CO", null),     // Cut Off
                createPlayer("hero_test_btn", "HeroTestBTN", 6, 20000, true, "BTN", Arrays.asList("As", "Ks")) // Hero在按钮位，底牌As Ks
            );

        request.setPlayers(players);

        String result =
            sendRequestWithFullLogging(ProtocolConstants.CREATE_HAND_REQ, request, 10, TimeUnit.SECONDS, "创建手牌");

        JsonNode responseJson = objectMapper.readTree(result);
        
        // 验证新的响应格式：rqid, code, desc, data
        if (!responseJson.has("data")) {
            throw new IllegalArgumentException("Response missing 'data' field");
        }
        if (!responseJson.has("rqid") || !responseJson.has("code") || !responseJson.has("desc")) {
            logger.warn("[{}] 响应格式不完整: {}", CONNECTION_ID, result);
        }

        int code = responseJson.get("code").asInt();
        String desc = responseJson.get("desc").asText();
        JsonNode dataNode = responseJson.get("data");
        if (code == 200) {
            
            // 初始化盲注投入（自动记录SB和BB的投入）
            playerInvestments.put("test_sb_001", new BigDecimal("100"));  // SB投入100
            playerInvestments.put("test_bb_002", new BigDecimal("200"));  // BB投入200
            
            logger.info("[{}] [HandID:{}] ✅ 手牌创建成功", CONNECTION_ID, currentHandIdStr);
            logger.info("[{}] [HandID:{}] 📋 6人桌配置: SB(20000), BB(19800), UTG(21500), MP(18200), CO(22800), Hero(20000)",
                CONNECTION_ID, currentHandIdStr);
            logger.info("[{}] [HandID:{}] 💰 盲注: 100/200, Hero底牌: As Ks - 强起手牌，适合加注", CONNECTION_ID, currentHandIdStr);
        } else {
            throw new RuntimeException("创建手牌失败: code=" + code + ", desc=" + desc + ", result=" + result);
        }
    }

    private static void step3_RecordPreflopActions() throws Exception {
        logger.info("[{}] [HandID:{}] \n步骤3: Preflop行动阶段 - 与RealPokerHandFlowTest完全一致", CONNECTION_ID,
            currentHandIdStr);

        // UTG fold (seat 3)
        waitForInput("按回车执行: UTG fold");
        recordActionComplete("test_utg_003", 3, "fold", null, new BigDecimal("21500"), new BigDecimal("300"), 
            "枪口位弃牌，手牌不够强或不想冒险");

        // MP call 200 (seat 4)
        waitForInput("按回车执行: MP call 200");
        recordActionComplete("test_mp_004", 4, "call", new BigDecimal("200"), new BigDecimal("18000"),
            new BigDecimal("500"), "中位玩家跟大盲，想看翻牌，底池现300(盲注)+200=500");

        // CO fold (seat 5)
        waitForInput("按回车执行: CO fold");
        recordActionComplete("test_co_005", 5, "fold", null, new BigDecimal("22800"), new BigDecimal("500"),
            "关煞位弃牌，位置虽好但牌力不足");

        // Hero(BTN) 行动决策点 - Preflop
        consultAIForHeroAction("Preflop", "前面玩家：UTG弃牌, MP跟注200, CO弃牌", 
            "建议行动", "raise", new BigDecimal("600"));
        
        waitForInput("按回车执行: Hero(BTN) raise to 600");
        recordActionComplete("hero_test_btn", 6, "raise", new BigDecimal("600"), new BigDecimal("19400"),
            new BigDecimal("1100"));
        logger.info("[{}] [HandID:{}]   ✓ Hero(BTN) raise to 600 - 按钮位持AKs加注，利用位置优势和强牌力，底池500+600=1100", CONNECTION_ID,
            currentHandIdStr);

        // SB fold (seat 1)
        waitForInput("按回车执行: SB fold");
        recordActionComplete("test_sb_001", 1, "fold", null, new BigDecimal("19900"), new BigDecimal("1100"));

        // BB call 400 to complete to 600 (seat 2)
        waitForInput("按回车执行: BB call 400");
        recordActionComplete("test_bb_002", 2, "call", new BigDecimal("400"), new BigDecimal("19200"),
            new BigDecimal("1500"));
        logger.info("[{}] [HandID:{}]   ✓ BB call 400 - 大盲位补齐到600(已投200+现补400)，底池1100+400=1500", CONNECTION_ID, currentHandIdStr);

        // MP call 400 to complete to 600 (seat 4)
        waitForInput("按回车执行: MP call 400");
        recordActionComplete("test_mp_004", 4, "call", new BigDecimal("400"), new BigDecimal("17600"),
            new BigDecimal("1900"));
        logger.info("[{}] [HandID:{}]   ✓ MP call 400 - 中位补齐到600(已投200+现补400)，3人入池，底池1500+400=1900", CONNECTION_ID, currentHandIdStr);

        logger.info("[{}] [HandID:{}] 🎉 Preflop完成: 3人到翻牌(BB/MP/Hero)，底池1900", CONNECTION_ID, currentHandIdStr);
        logger.info("[{}] [HandID:{}] 📊 剩余筹码 - BB(19200), MP(17600), Hero(19400)", CONNECTION_ID,
            currentHandIdStr);
    }

    private static void step4_TransitionToFlop() throws Exception {
        logger.info("[{}] [HandID:{}] \n🔄 步骤4: Flop转换 - 发放3张公共牌", CONNECTION_ID, currentHandIdStr);

        Map<String, Object> requestData = Map.of("hand_id", currentHandIdStr, "street", "flop", "board_cards",
            Arrays.asList("Ah", "7s", "2c"), "active_players", 3, "pot_after", new BigDecimal("1900"));

        String result =
            sendRequestWithFullLogging(ProtocolConstants.TRANSITION_STREET_REQ, requestData, 10, TimeUnit.SECONDS,
                "Flop转换");

        currentStreet = "flop";  // 更新当前街道
        logger.info("[{}] [HandID:{}]  ✅ 转换到Flop: Ah 7s 2c - Hero击中顶对A(配对)+K踢脚，牌力很强，应主动下注建立价值", CONNECTION_ID,
            currentHandIdStr);
    }

    private static void step5_RecordFlopActions() throws Exception {
        logger.info("[{}] [HandID:{}] \n📍 步骤5: Flop行动阶段 - 与RealPokerHandFlowTest完全一致", CONNECTION_ID,
            currentHandIdStr);

        // BB check (seat 2)
        waitForInput("按回车执行: BB check");
        recordActionComplete("test_bb_002", 2, "check", null, new BigDecimal("19200"), new BigDecimal("1900"));
        logger.info("[{}] [HandID:{}]   ✓ BB check - 大盲位置不利，先过牌观察对手，底池保持1900", CONNECTION_ID, currentHandIdStr);

        // MP check (seat 4)
        waitForInput("按回车执行: MP check");
        recordActionComplete("test_mp_004", 4, "check", null, new BigDecimal("17600"), new BigDecimal("1900"));
        logger.info("[{}] [HandID:{}]   ✓ MP check - 中位也过牌，将行动权交给有位置优势的Hero", CONNECTION_ID, currentHandIdStr);

        // Hero 行动决策点 - Flop (Ah 7s 2c)
        consultAIForHeroAction("Flop", "公共牌：Ah 7s 2c，前面玩家：BB过牌, MP过牌", 
            "建议行动", "bet", new BigDecimal("1200"));
        
        waitForInput("按回车执行: Hero bet 1200");
        recordActionComplete("hero_test_btn", 6, "bet", new BigDecimal("1200"), new BigDecimal("18200"),
            new BigDecimal("3100"));
        logger.info("[{}] [HandID:{}]   ✓ Hero bet 1200 - 按钮位持顶对主动下注，约2/3底池(1200/1900)，既能保护手牌又能建立价值，底池1900+1200=3100", CONNECTION_ID,
            currentHandIdStr);

        // BB fold (seat 2)
        waitForInput("按回车执行: BB fold");
        recordActionComplete("test_bb_002", 2, "fold", null, new BigDecimal("19200"), new BigDecimal("3100"));
        logger.info("[{}] [HandID:{}]   ✓ BB fold - 大盲位面临下注弃牌，可能没击中牌面，退出本手牌", CONNECTION_ID, currentHandIdStr);

        // MP call 1200 (seat 4)
        waitForInput("按回车执行: MP call 1200");
        recordActionComplete("test_mp_004", 4, "call", new BigDecimal("1200"), new BigDecimal("16400"),
            new BigDecimal("4300"));
        logger.info("[{}] [HandID:{}]   ✓ MP call 1200 - 中位跟注1200，可能有听牌(顺子/同花)或中对，2人进入Turn，底池3100+1200=4300", CONNECTION_ID,
            currentHandIdStr);

        logger.info("[{}] [HandID:{}] 🎉 Flop完成: 2人到Turn(MP/Hero)，底池4300", CONNECTION_ID, currentHandIdStr);
        logger.info("[{}] [HandID:{}] 📊 剩余筹码 - MP(16400), Hero(18200)", CONNECTION_ID, currentHandIdStr);
    }

    private static void step6_TransitionToTurn() throws Exception {
        logger.info("[{}] [HandID:{}] \n🔄 步骤6: Turn转换 - 发放第4张公共牌", CONNECTION_ID, currentHandIdStr);

        Map<String, Object> requestData = Map.of("hand_id", currentHandIdStr, "street", "turn", "board_cards",
            Arrays.asList("Ah", "7s", "2c", "Kh"), "active_players", 2, "pot_after", new BigDecimal("4300"));

        String result =
            sendRequestWithFullLogging(ProtocolConstants.TRANSITION_STREET_REQ, requestData, 10, TimeUnit.SECONDS,
                "Turn转换");

        currentStreet = "turn";  // 更新当前街道
        logger.info("[{}] [HandID:{}]  ✅ 转换到Turn: Ah 7s 2c Kh - K发出，Hero持AK击中两对(AAKK)，牌力大幅提升，应持续施压", CONNECTION_ID,
            currentHandIdStr);
    }

    private static void step7_RecordTurnActions() throws Exception {
        logger.info("[{}] [HandID:{}] \n📍 步骤7: Turn行动阶段 - 与RealPokerHandFlowTest完全一致", CONNECTION_ID,
            currentHandIdStr);

        // MP check (seat 4)
        waitForInput("按回车执行: MP check");
        recordActionComplete("test_mp_004", 4, "check", null, new BigDecimal("16400"), new BigDecimal("4300"));
        logger.info("[{}] [HandID:{}]   ✓ MP check - 中位继续过牌，可能在听顺子或同花，底池保持4300", CONNECTION_ID, currentHandIdStr);

        // Hero 行动决策点 - Turn (Ah 7s 2c Kh)
        consultAIForHeroAction("Turn", "公共牌：Ah 7s 2c Kh，前面玩家：MP过牌", 
            "建议行动", "bet", new BigDecimal("2800"));
        
        waitForInput("按回车执行: Hero bet 2800");
        recordActionComplete("hero_test_btn", 6, "bet", new BigDecimal("2800"), new BigDecimal("15400"),
            new BigDecimal("7100"));
        logger.info("[{}] [HandID:{}]   ✓ Hero bet 2800 - 持两对AK价值下注，约65%底池(2800/4300)，既保护手牌又榨取价值，底池4300+2800=7100", CONNECTION_ID,
            currentHandIdStr);

        // MP call 2800 (seat 4)
        waitForInput("按回车执行: MP call 2800");
        recordActionComplete("test_mp_004", 4, "call", new BigDecimal("2800"), new BigDecimal("13600"),
            new BigDecimal("9900"));
        logger.info("[{}] [HandID:{}]   ✓ MP call 2800 - 中位继续跟注，底池已很大(7100+2800=9900)，可能有强听牌或中等牌力不愿弃牌", CONNECTION_ID, currentHandIdStr);

        logger.info("[{}] [HandID:{}] 🎉 Turn完成: 2人到River(MP/Hero)，底池9900", CONNECTION_ID, currentHandIdStr);
        logger.info("[{}] [HandID:{}] 📊 剩余筹码 - MP(13600), Hero(15400)", CONNECTION_ID, currentHandIdStr);
    }

    private static void step8_TransitionToRiver() throws Exception {
        logger.info("[{}] [HandID:{}] \n🔄 步骤8: River转换 - 发放第5张公共牌", CONNECTION_ID, currentHandIdStr);

        Map<String, Object> requestData = Map.of("hand_id", currentHandIdStr, "street", "river", "board_cards",
            Arrays.asList("Ah", "7s", "2c", "Kh", "4d"), "active_players", 2, "pot_after", new BigDecimal("9900"));

        String result =
            sendRequestWithFullLogging(ProtocolConstants.TRANSITION_STREET_REQ, requestData, 10, TimeUnit.SECONDS,
                "River转换");

        currentStreet = "river";  // 更新当前街道
        logger.info("[{}] [HandID:{}]  ✅ 转换到River: Ah 7s 2c Kh 4d - 4d是安全牌(blank)，没有完成听牌，Hero两对AK仍然极强，应继续下注榨取价值",
            CONNECTION_ID, currentHandIdStr);
    }

    private static void step9_RecordRiverActions() throws Exception {
        logger.info("[{}] [HandID:{}] \n📍 步骤9: River行动阶段 - 最终决战", CONNECTION_ID, currentHandIdStr);

        // MP check (seat 4)
        waitForInput("按回车执行: MP check");
        recordActionComplete("test_mp_004", 4, "check", null, new BigDecimal("13600"), new BigDecimal("9900"));
        logger.info("[{}] [HandID:{}]   ✓ MP check - 中位River继续过牌，可能听牌未中(比如顺子听牌)，或牌力不强不敢下注", CONNECTION_ID, currentHandIdStr);

        // Hero 行动决策点 - River (Ah 7s 2c Kh 4d)
        consultAIForHeroAction("River", "公共牌：Ah 7s 2c Kh 4d，前面玩家：MP过牌", 
            "建议行动", "bet", new BigDecimal("5000"));
        
        waitForInput("按回车执行: Hero bet 5000");
        recordActionComplete("hero_test_btn", 6, "bet", new BigDecimal("5000"), new BigDecimal("10400"),
            new BigDecimal("14900"));
        logger.info("[{}] [HandID:{}]   ✓ Hero bet 5000 - 持两对AK价值下注，约50%底池(5000/9900)，榨取最后价值，底池9900+5000=14900", CONNECTION_ID,
            currentHandIdStr);

        // MP fold (seat 4) - 无法跟注大额下注
        waitForInput("按回车执行: MP fold");

        recordActionComplete("test_mp_004", 4, "fold", null, new BigDecimal("13600"), new BigDecimal("14900"));
        logger.info("[{}] [HandID:{}]   ✓ MP fold - 中位弃牌，可能听牌未中或牌力不足，Hero不用摊牌直接获胜", CONNECTION_ID, currentHandIdStr);

        logger.info("[{}] [HandID:{}]  ✅ River完成: Hero获胜", CONNECTION_ID, currentHandIdStr);
        logger.info("[{}] [HandID:{}] 🏆 Hero赢得底池9900(对手fold，不包括Hero最后下注5000)", CONNECTION_ID, currentHandIdStr);
        logger.info("[{}] [HandID:{}]  📊 Hero总投入: 600(preflop) + 1200(flop) + 2800(turn) = 4600",
            CONNECTION_ID, currentHandIdStr);
        logger.info("[{}] [HandID:{}]  💰 Hero净收益: 9900(赢得) - 4600(总投入) = +5300", CONNECTION_ID,
            currentHandIdStr);
    }

    private static void step10_AIAnalysisSkipped() throws Exception {
        logger.info("[{}] [HandID:{}] \n🤖 步骤10: AI分析 (智能跳过)", CONNECTION_ID, currentHandIdStr);
        logger.info("[{}] [HandID:{}]  AI分析跳过 - 当前不是Hero决策时机，继续游戏流程", CONNECTION_ID,
            currentHandIdStr);
    }

    private static void step11_CompleteHand() throws Exception {
        logger.info("[{}] [HandID:{}] \n步骤11: 完成手牌 - 结算和统计", CONNECTION_ID, currentHandIdStr);

        // ✅ 已移除 hero_net_result 字段，不再需要提供Hero特定的结果
        Map<String, Object> requestData =
            Map.of("hand_id", currentHandIdStr, 
                "completion_type", "player_fold",               // 对手弃牌，玩家获胜
                "final_pot", new BigDecimal("9900")             // 与RealPokerHandFlowTest一致: 9900
            );

        // 格式化请求JSON（紧凑单行）
        String requestJson = compactMapper.writeValueAsString(requestData);
        
        // 发送请求
        CompletableFuture<String> responseFuture = 
            client.sendMessage(ProtocolConstants.COMPLETE_HAND_REQ, requestData, 10, TimeUnit.SECONDS);
        String responseJson = responseFuture.get();
        
        // 解析响应
        JsonNode responseNode = objectMapper.readTree(responseJson);
        int code = responseNode.get("code").asInt();
        String desc = responseNode.get("desc").asText();
        String reqId = responseNode.has("rqid") ? responseNode.get("rqid").asText() : "N/A";
        
        // 计算起始筹码（假设所有玩家初始都是20000）
        BigDecimal initialStack = new BigDecimal("20000");
        BigDecimal heroNetResult = new BigDecimal("5300");
        BigDecimal finalPot = new BigDecimal("9900");
        BigDecimal heroInvestment = finalPot.subtract(heroNetResult);  // 9900 - 5300 = 4600
        BigDecimal heroFinalStack = initialStack.add(heroNetResult);    // 20000 + 5300 = 25300
        
        // 打印结构化的结算信息
        logger.info("\n" +
            "================================================================================\n" +
            "[{}] [HandID:{}] [手牌结算] 盈亏统计\n" +
            "--------------------------------------------------------------------------------\n" +
            "结算类型: {}\n" +
            "Hero起始筹码: {}\n" +
            "Hero累计投入: {}\n" +
            "最终底池: {}\n" +
            "Hero净收益: {}\n" +
            "Hero最终筹码: {}\n" +
            "--------------------------------------------------------------------------------\n" +
            "请求: {}\n" +
            "返回: code={}, desc={}, reqId={}\n" +
            "================================================================================",
            CONNECTION_ID, currentHandIdStr,
            "player_fold (对手弃牌，Hero获胜)",
            initialStack,
            heroInvestment,
            finalPot,
            heroNetResult.compareTo(BigDecimal.ZERO) >= 0 ? "+" + heroNetResult : heroNetResult,
            heroFinalStack,
            requestJson,
            code,
            desc,
            reqId
        );
        
        // 打印结果
        if (code == 200) {
            logger.info("[{}] [HandID:{}] >>> 结算成功 ✓ Hero赢得 +{} 筹码\n", 
                CONNECTION_ID, currentHandIdStr, heroNetResult);
        } else {
            logger.error("[{}] [HandID:{}] >>> 结算失败 ✗ - {}\n", CONNECTION_ID, currentHandIdStr, desc);
        }
    }

    /**
     * 记录完整的行动请求 - 优化后的版本（移除seat_number，添加hand_id）
     */
    private static void recordActionComplete(String playerId, int seatNumber, String actionType, BigDecimal amount,
        BigDecimal stackAfter, BigDecimal potAfter) throws Exception {
        recordActionComplete(playerId, seatNumber, actionType, amount, stackAfter, potAfter, null);
    }
    
    private static void recordActionComplete(String playerId, int seatNumber, String actionType, BigDecimal amount,
        BigDecimal stackAfter, BigDecimal potAfter, String description) throws Exception {
        BigDecimal finalAmount = (amount == null) ? BigDecimal.ZERO : amount;
        
        // V4.1: 计算before_stack和before_pot
        BigDecimal stackBefore;
        BigDecimal potBefore;
        
        // 从投资记录中获取玩家当前投入
        BigDecimal playerInvestment = playerInvestments.getOrDefault(playerId, BigDecimal.ZERO);
        
        // 计算before_stack（行动前筹码）
        if ("fold".equalsIgnoreCase(actionType) || "check".equalsIgnoreCase(actionType)) {
            stackBefore = stackAfter;  // fold/check不消耗筹码
        } else {
            stackBefore = stackAfter.add(finalAmount);  // 其他行动：before = after + amount
        }
        
        // 计算before_pot（行动前底池）
        if ("fold".equalsIgnoreCase(actionType) || "check".equalsIgnoreCase(actionType)) {
            potBefore = potAfter;  // fold/check不改变底池
        } else {
            potBefore = potAfter.subtract(finalAmount);  // 其他行动：before = after - amount
        }
        
        Map<String, Object> requestData = Map.of(
            "hand_id", currentHandIdStr, 
            "player_id", playerId, 
            "action_type", actionType, 
            "street", currentStreet,
            "amount", finalAmount,
            "before_stack", stackBefore,
            "after_stack", stackAfter,
            "before_pot", potBefore,
            "after_pot", potAfter
        );

        // 生成简洁的行动描述（包含解释信息）
        String actionDesc;
        if (description != null && !description.isEmpty()) {
            actionDesc = String.format("%s %s%s - %s", 
                playerId, 
                actionType, 
                finalAmount.compareTo(BigDecimal.ZERO) > 0 ? " " + finalAmount : "",
                description);
        } else {
            actionDesc = String.format("%s %s%s", 
                playerId, 
                actionType, 
                finalAmount.compareTo(BigDecimal.ZERO) > 0 ? " " + finalAmount : "");
        }
        
        sendActionWithSimpleLogging(requestData, actionDesc.trim());
    }

    /**
     * 创建玩家信息 - 前端驱动模式，只提供基础信息
     * 注意：已移除isHero参数，任何玩家都可以提供底牌
     */
    private static CreateHandRequest.PlayerInfo createPlayer(String playerId, String playerName, int seatNumber,
        int stack, boolean isHero, String position, List<String> holeCards) {
        CreateHandRequest.PlayerInfo player = new CreateHandRequest.PlayerInfo();
        player.setPlayerId(playerId);
        // V4.1: player_name字段已移除
        player.setSeatNumber(seatNumber);
        player.setStack(BigDecimal.valueOf(stack));
        // player.setIsHero(isHero);  // ❌ 已移除：不再需要标记Hero
        player.setPosition(position);
        // V4.1: hole_cards字段已移除（改为AI分析时提供）
        // actionOrder 不设置，体现前端驱动理念
        return player;
    }

    private static void waitForInput() {
        System.out.print("按回车继续下一步...");
        scanner.nextLine();
    }
    
    private static void waitForInput(String prompt) {
        System.out.print(prompt + "...");
        scanner.nextLine();
    }

    /**
     * Hero行动AI决策咨询
     * 
     * @param street 当前街道
     * @param situation 当前局面描述
     * @param defaultAction 默认建议行动
     * @param actionType 默认行动类型
     * @param amount 默认行动金额
     */
    private static void consultAIForHeroAction(String street, String situation, String defaultAction, 
                                               String actionType, BigDecimal amount) throws Exception {
        
        logger.info("");
        logger.info("[{}] [HandID:{}] 🤖 === Hero决策咨询点 ===", CONNECTION_ID, currentHandIdStr);
        logger.info("[{}] [HandID:{}] 📍 当前街道: {}", CONNECTION_ID, currentHandIdStr, street);
        logger.info("[{}] [HandID:{}] 🎯 局面描述: {}", CONNECTION_ID, currentHandIdStr, situation);
        logger.info("[{}] [HandID:{}] 💡 系统建议: {} ({})", CONNECTION_ID, currentHandIdStr, 
            actionType.toUpperCase(), amount);
        logger.info("[{}] [HandID:{}] ❓ 是否需要AI决策分析？", CONNECTION_ID, currentHandIdStr);
        
        System.out.print("\n请选择 (a=AI分析, s=跳过使用系统建议): ");
        String choice = scanner.nextLine().trim().toLowerCase();
        
        if ("a".equals(choice) || "ai".equals(choice)) {
            logger.info("[{}] [HandID:{}] ✅ 用户选择: 请求AI分析", CONNECTION_ID, currentHandIdStr);
            requestAIAnalysisForHero(street, situation);
        } else {
            logger.info("[{}] [HandID:{}] ⏭️  用户选择: 跳过AI分析，使用系统建议", CONNECTION_ID, currentHandIdStr);
            logger.info("[{}] [HandID:{}] 📝 系统建议行动: {} {}", CONNECTION_ID, currentHandIdStr, 
                actionType.toUpperCase(), amount);
        }
        
        logger.info("");
    }

    /**
     * 请求AI分析
     * 注意：已更新为新的API格式，使用 player_id + hole_cards
     */
    private static void requestAIAnalysisForHero(String street, String situation) throws Exception {
        logger.info("[{}] [HandID:{}] 🔮 正在请求AI分析...", CONNECTION_ID, currentHandIdStr);
        
        try {
            // V4.1: 构建AI分析请求（使用新的字段名：hand_id + player_id + hole_cards）
            Map<String, Object> aiRequestData = Map.of(
                "hand_id", currentHandIdStr,  // ✅ V4.1: 使用hand_id（String）
                "player_id", "hero_test_btn",  // ✅ 新字段：player_id (任意玩家)
                "hole_cards", Arrays.asList("As", "Ks")  // ✅ 新字段：该玩家的底牌
            );
            
            String aiResult = sendRequestWithFullLogging(
                ProtocolConstants.AI_ANALYZE_REQ, 
                aiRequestData, 
                15, 
                TimeUnit.SECONDS,
                "AI决策分析"
            );
            
            // 解析并显示AI分析结果
            displayAIAnalysisResult(aiResult, street);
            
        } catch (Exception e) {
            logger.error("[{}] [HandID:{}] ❌ AI分析失败: {}", CONNECTION_ID, currentHandIdStr, e.getMessage());
            logger.info("[{}] [HandID:{}] 🔄 将使用系统默认建议继续游戏", CONNECTION_ID, currentHandIdStr);
        }
    }

    /**
     * 显示AI分析结果
     */
    private static void displayAIAnalysisResult(String aiResultJson, String street) throws Exception {
        try {
            // 解析AI响应
            ObjectMapper mapper = new ObjectMapper();
            JsonNode response = mapper.readTree(aiResultJson);
            
            if (response.has("data") && !response.get("data").isNull()) {
                JsonNode data = response.get("data");
                
                logger.info("[{}] [HandID:{}] 🎯 === AI分析结果 ({}) ===", CONNECTION_ID, currentHandIdStr, street);
                
                // 显示推荐行动
                if (data.has("action")) {
                    String aiAction = data.get("action").asText();
                    String aiSize = data.has("size") ? data.get("size").asText() : "0";
                    logger.info("[{}] [HandID:{}] 💡 AI推荐行动: {} {}", CONNECTION_ID, currentHandIdStr,
                        aiAction.toUpperCase(), !"0".equals(aiSize) ? aiSize : "");
                }
                
                // 显示分析理由（reasons现在是单个字符串）
                if (data.has("reasons")) {
                    String reasons = data.get("reasons").asText();
                    if (reasons != null && !reasons.isEmpty()) {
                        logger.info("[{}] [HandID:{}] 📖 分析理由: {}", CONNECTION_ID, currentHandIdStr, reasons);
                    }
                }
                
                logger.info("[{}] [HandID:{}] =============================", CONNECTION_ID, currentHandIdStr);
                
            } else {
                logger.warn("[{}] [HandID:{}] ⚠️  AI分析响应无数据，使用系统默认建议", CONNECTION_ID, currentHandIdStr);
            }
            
        } catch (Exception e) {
            logger.error("[{}] [HandID:{}] ❌ 解析AI分析结果失败: {}", CONNECTION_ID, currentHandIdStr, e.getMessage());
            logger.info("[{}] [HandID:{}] 🔄 将使用系统默认建议继续游戏", CONNECTION_ID, currentHandIdStr);
        }
        
        // 等待用户确认后继续
        System.out.print("按回车继续执行行动...");
        scanner.nextLine();
    }
}
